const fs = require("fs");
const path = require("path");
// @babel/parser 分析js代码
const parser = require("@babel/parser");
// 使用traverse解析AST语法树, 使用的是commonjs语法，要使用default
const traverse = require("@babel/traverse").default;
const babel = require("@babel/core");
const moduleAnalyser = (filename) => {
  const content = fs.readFileSync(filename, "utf8");
  // 使用babel/parser编译js 代码, 生成js的AST对象
  const ASTObject = parser.parse(content, {
    sourceType: "module",
  });
  //   console.log(ASTObject);
  //   console.log(ASTObject.program.body);
  // traverse分析AST的node节点，如果node属性type为ImportDeclaration，则进行特殊处理
  const dependencies = {};
  traverse(ASTObject, {
    // 查找出import语句
    ImportDeclaration({ node }) {
      // console.log(node);
      const dirname = path.dirname(filename);
      //  生成相对路径
      const newFile = "./" + path.join(dirname, node.source.value);
      dependencies[node.source.value] = newFile;
    },
  });
  // 使用babel/core转化AST为es语法
  const { code } = babel.transformFromAst(ASTObject, null, {
    // 转化es6到es5，以及其他js版本都转化
    presets: ["@babel/preset-env"],
  });
  //   console.log(code);
  return {
    filename,
    dependencies,
    code,
  };
};

// 根据入口，循环遍历查找出项目下所有的依赖关系图谱
const makeDependenciesGraph = (entry) => {
  const entryModule = moduleAnalyser(entry);
  const graphArray = [entryModule];
  for (let i = 0; i < graphArray.length; i++) {
    const item = graphArray[i];
    const { dependencies } = item;
    if (dependencies) {
      for (let j in dependencies) {
        graphArray.push(moduleAnalyser(dependencies[j]));
      }
    }
  }
  const graphModule = Object.create(null);
  graphArray.forEach((item) => {
    graphModule[item.filename] = {
      dependencies: item.dependencies,
      code: item.code,
    };
  });
  //   console.log(graphArray);
  return graphModule;
};
// console.log(makeDependenciesGraph("./src/index.js"));

// 分析Dependencies，生成可以运行的code代码
const generateCode = (entry) => {
  const graph = JSON.stringify(makeDependenciesGraph(entry));
  /* 
  1.自定义require函数
  2.定义localRequire函数，通过dependencies对象(传入路径的地址，即dependencies的key)解析出绝对路径，
   */
  return `
    (function(graph){
      function require(module){
        function localRequire(relativePath){
          return require(graph[module].dependencies[relativePath])
        }
        let exports ={};
        (function(require, exports, code){
            eval(code);
        })(localRequire, exports, graph[module].code);
        return exports;
      }
      require('${entry}');
    })(${graph});
  `;
};
console.log(generateCode("./src/index.js"));

const moduleInfo = moduleAnalyser("./src/index.js");
